/* visStreamsurface.cpp
 * 
*/

#include "visStreamsurface.h"
namespace visualization
{

//=======================================================
void visStreamsurface::setPointSources(int pointsNum, 
									   double sources[])
//=======================================================
{}

//====================================================================
void visStreamsurface::setMultiplePointSources1(int surfaceNum, 
											    int stlInOneSurface[], 
											    double **sources)
//====================================================================
{
	nStreamsurface = surfaceNum;
	streamlinesInOneSurface = stlInOneSurface;

	streamsurface = new visStreamline[nStreamsurface];
	for(int i = 0; i < nStreamsurface; i++)
		streamsurface[i].setDataField(dataField);

	if(1 == makeCircle)
	{
		for(int i = 0; i < nStreamsurface; i++)
			streamsurface[i].makeSourceFormCircle();
	}
	switch(integrationDirection)
	{
	case 0:
		for(int i = 0; i < nStreamsurface; i++)
		{
			streamsurface[i].setIntegrationDouble();
		}
		break;
	case 1:
		for(int i = 0; i < nStreamsurface; i++)	
		{
			streamsurface[i].setIntegrationForward();
		}
		break;
	case -1:
		for(int i = 0; i < nStreamsurface; i++)
		{
			streamsurface[i].setIntegrationBackward();
		}
		break;
	}

	int index = 0;
	for(int i = 0; i < nStreamsurface; i++)
	{
		visualization::Point *tmp = new visualization::Point[stlInOneSurface[i]];
		for(int j = 0; j < stlInOneSurface[i]; j++)
		{
			tmp[j].setCoordinates(sources[index][0], sources[index][1], sources[index][2]);
			index++;
		}
		streamsurface[i].setSources(tmp, stlInOneSurface[i]);
		delete []tmp;
	}
}

//===================================================================
void visStreamsurface::setMultiplePointSources2(int surfaceNum, 
											   int commonNumber,
											   double **sources)
//===================================================================
{
	int *tmp = new int(surfaceNum);
	for(int i = 0; i < surfaceNum; i++)
		tmp[i] = commonNumber;
	this->setMultiplePointSources1(surfaceNum, tmp, sources);
}

//====================================================
void visStreamsurface::setLineSources(int surfaceNum, 
									  double *sourceX, 
									  double *sourceY)
//====================================================
{}

void visStreamsurface::setMultipleLineSources()
//=============================================
{}

/**The following simply invoke corresponding functions in class visStreamline**/

// Only used for streamtube
void visStreamsurface::makeSourcesFormCircle()
{
	makeCircle = 1;
}

// Backward integration.
void visStreamsurface::setIntegrationBackward()
{	
	integrationDirection = -1;
}

// Forward integration.
void visStreamsurface::setIntegrationForward()
{	
	integrationDirection = 1;
}

// Both directions.
void visStreamsurface::setIntegrationDouble()
{
	integrationDirection = 0;
}

// Integration parameters.
void visStreamsurface::setParameters(double step,
									 double maxPrpgtTime)
{
	stepSize = step;
	maxPropagationTime = maxPrpgtTime;
	for(int i = 0; i < nStreamsurface; i++)
	{
		streamsurface[i].setParameters(step, maxPrpgtTime);
	}
}
/****The above simply invoke corresponding functions in class visStreamline****/

//=======================================================================
int visStreamsurface::longerDiagonal(vector<visualization::Point> *sl1,
									 unsigned startIndex1,
									 vector<visualization::Point> *sl2,
									 unsigned startIndex2)
//=======================================================================
{
	// Outof range..
	if(startIndex1 >= sl1->size() - 1 || startIndex2 >= sl2->size() - 1)	
	{
		return 0;
	}

	double diagDist1  = pow((*sl1)[startIndex1].getX() - (*sl2)[startIndex2 + 1].getX(), 2);
		   diagDist1 += pow((*sl1)[startIndex1].getY() - (*sl2)[startIndex2 + 1].getY(), 2);
		   diagDist1 += pow((*sl1)[startIndex1].getZ() - (*sl2)[startIndex2 + 1].getZ(), 2);

	double diagDist2  = pow((*sl2)[startIndex2].getX() - (*sl1)[startIndex1 + 1].getX(), 2);
		   diagDist2 += pow((*sl2)[startIndex2].getY() - (*sl1)[startIndex1 + 1].getY(), 2);
		   diagDist2 += pow((*sl2)[startIndex2].getZ() - (*sl1)[startIndex1 + 1].getZ(), 2);
		   
	if(diagDist1 > diagDist2)
	{
		return 1;
	}
	return 2;
}

//====================================================================
double visStreamsurface::pointsDist(vector<visualization::Point> *sl1,
									unsigned startIndex1,
									vector<visualization::Point> *sl2,
									unsigned startIndex2)
//====================================================================
{
	double dist  = pow((*sl1)[startIndex1].getX() - (*sl2)[startIndex2].getX(), 2);
		   dist += pow((*sl1)[startIndex1].getY() - (*sl2)[startIndex2].getY(), 2);
		   dist += pow((*sl1)[startIndex1].getZ() - (*sl2)[startIndex2].getZ(), 2);
    
    dist = sqrt(dist);
    return dist;
}

//========================================================================================
inline void visStreamsurface::bridgeTwoStreamLine(vector<visualization::Point> *sl1,
												  unsigned startIndex1,
												  vector<visualization::Point> *sl2,
												  unsigned startIndex2,
												  int &triIndex,
												  int startingPointOrNot
												  )
//========================================================================================
{
	unsigned i = startIndex1, j = startIndex2;
	int k = 1;
	while(i < sl1->size() - 1 && j < sl2->size() - 1)
	{
		triangles -> push_back((*sl1)[i]);
		triangles -> push_back((*sl2)[j]);
		if(k)
		{
			i++;
			triangles -> push_back((*sl1)[i]);
			k = 0;
		}
		else
		{
			j++;
			triangles -> push_back((*sl2)[j]);
			k = 1;
		}
	}
////	vector<visualization::Point> tmp;
//	int selectedSide = longerDiagonal(sl1, startIndex1, sl2, startIndex2);
//	
//	if(!selectedSide)
//		return;
//	
//	/* Determine which point on one of the two streamlines is 
//	 * to be inserted to triangles first.
//
//	 * At the same time, calculate the ratio.
//    */
//	//======================================
//	if(startingPointOrNot)
//	//======================================
//	{
//		// Initialize the two distance variables
//		minDist = pointsDist(sl1, startIndex1, sl2, startIndex2);
//		maxDist = minDist;
//
//		/* If the current points are starting poinst,
//		 * determine whether the distantce is too long,
//		 * if so, return
//		*/
//		// Distances of the two diagonals
//	    if(1 == selectedSide)
//		// Insert the point on sl1 and continue
//		{
//			//tmp.push_back((*sl1)[startIndex1]);
//			//triangles.push_back(tmp);
//			//tmp.clear();
//
//			triangles.push_back((*sl1)[startIndex1]);
//			triangles.push_back((*sl1)[startIndex1 + 1]);
//			triangles.push_back((*sl2)[startIndex2]);
//
//			bridgeTwoStreamLine(sl1, startIndex1 + 1, sl2, startIndex2, triIndex, 0);
//		}
//		else
//		// Insert the point on sl2 and continue
//		{
//			//tmp.push_back((*sl2)[startIndex2]);
//			//triangles.push_back(tmp);
//			//tmp.clear();
//			
//			triangles.push_back((*sl1)[startIndex1]);
//			triangles.push_back((*sl2)[startIndex2]);
//			triangles.push_back((*sl2)[startIndex2 + 1]);
//
//			bridgeTwoStreamLine(sl1, startIndex1, sl2, startIndex2 + 1, triIndex, 0);
//		}
//	}	
//	else
//	/* Perform the splitting and "merging" strategy or act
//     * normally to construct the surface
//    */
//	{
//		double dist = pointsDist(sl1, startIndex1, sl2, startIndex2);
//
//        double r1 = maxDist / dist;
//		double r2 = dist / minDist;
//
//		// Modify the minDist and maxDist
//		if(dist < minDist)
//			minDist = dist;
//		if(dist > maxDist)
//			maxDist = dist;
//
///* Via r1 and r2, it can be determined whether it's needed
// * to "merge" or split the surface. If none,continue the 
// * constuction process		 
//*/
//		size_t triSize = 0;
//		// It's needed to split the surface
//		if(r2 >= ratio)
//		{
//			/*1. Calculate the middle point
//			 *2. Push current two points to triangles!!!
//			 *3. Calculate the streamline passing the point(note that
//			 * direction should be considered)
//			 *4. Set the startingPointOrNot to 1, and go on
//		    */
//			visualization::Point midPt = (*sl1)[startIndex1] + (*sl2)[startIndex2];
//			midPt = midPt * 0.5;
//
//			visStreamline midLine(dataField);
//			midLine.setSources(&midPt, 1);
//			//midLine.setSources(midPt.getX(), midPt.getY(), midPt.getZ(), 1);
//			midLine.setParameters(stepSize, maxPropagationTime);
//			switch(directionFlag)
//			{
//			case 0:
//				midLine.setIntegrationDouble();
//			case 1:
//				midLine.setIntegrationForward();
//			case -1:
//				midLine.setIntegrationBackward();
//			}
//			intgResults *middleLine = midLine.integrate();
//
//			bridgeTwoStreamLine(sl1, startIndex1, &(*middleLine)[0], 0, triIndex, 1);
//			bridgeTwoStreamLine(&(*middleLine)[0], 0, sl2, startIndex2, triIndex, 1);
//		}
//		else
//		{
//			/* It's necessary to "merge", in fact, to increase
//			 * the step size on the two streamlines that are used
//			 * to construct the surface.
//			*/
//			if(r1 >= ratio)
//			{
//				startIndex1++;
//				startIndex2++;
//				selectedSide = longerDiagonal(sl1, startIndex1, sl2, startIndex2);
//
//				if(!selectedSide)
//				{
//					startIndex1--;
//					startIndex2--;
//				}
//			}
//
//			triSize = triangles.size() - 1;
//			if(1 == selectedSide)
//			{
//				triangles.push_back(triangles[triSize]);
//				triangles.push_back(triangles[triSize - 1]);
//				triangles.push_back((*sl1)[startIndex1 + 1]);
//				
//				bridgeTwoStreamLine(sl1, startIndex1 + 1, sl2, startIndex2, triIndex, 0);
//			}
//			else
//			{
//				triangles.push_back(triangles[triSize]);
//				triangles.push_back(triangles[triSize - 1]);
//				triangles.push_back((*sl2)[startIndex2 + 1]);
//
//				bridgeTwoStreamLine(sl1, startIndex1, sl2, startIndex2 + 1, triIndex, 0);
//			}
//		}
//	}
}

//==============================================
void visStreamsurface::constructStreamSurface()
//==============================================
{	
	for(int i = 0; i < nStreamsurface; i++)
		streamsurface[i].integrate();

	int index = 0;
	if(0 != integrationDirection)
	{
		directionFlag = integrationDirection;
		for(int j = 0; j < nStreamsurface; j++)
		{
			for(int i = 1; i <= streamlinesInOneSurface[j] + makeCircle - 1; i++)
			{
				bridgeTwoStreamLine(streamsurface[j].getStreamline(i), 0, 
									streamsurface[j].getStreamline(i+1), 0, 
									index, 1);
			}
		}
	}
	else
	{		
		for(int j = 0; j < nStreamsurface; j++)
		{
			for(int i = 1; i <= streamlinesInOneSurface[j] + makeCircle - 1; i++)
			{
				directionFlag = 1;
				bridgeTwoStreamLine(streamsurface[j].getStreamline(i), 0,
									streamsurface[j].getStreamline(i+1), 0, 
									index, 1);
			}
			for(int i = streamlinesInOneSurface[j] + makeCircle + 1; 
				i <= 2 * (streamlinesInOneSurface[j] + makeCircle) - 1;
				i++)
			{
				directionFlag = -1;
				bridgeTwoStreamLine(streamsurface[j].getStreamline(i), 0,
									streamsurface[j].getStreamline(i+1), 0, 
									index, 1);			
			}
		}
	}
}
}

